package com.meilai.project.service.business.intentionScheme;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.meilai.project.controller.web.market.entity.WorkOrderShootRequirement;
import com.meilai.project.controller.web.market.service.WorkOrderShootRequirementService;
import com.meilai.project.controller.web.market.vo.BillMarketDetailVO;
import com.meilai.project.core.bean.Pair;
import com.meilai.project.dto.business.intentionScheme.*;
import com.meilai.project.entity.business.building.BuildingArea;
import com.meilai.project.entity.business.combo.GasStationCombo;
import com.meilai.project.entity.business.installOrder.WorkOrder;
import com.meilai.project.entity.business.installOrder.WorkOrderInstallDetail;
import com.meilai.project.entity.business.installOrder.WorkOrderInstallDetailWorker;
import com.meilai.project.entity.business.installOrder.WorkerSubmit;
import com.meilai.project.entity.business.intentionScheme.VGasStationComboRuleMediaTypeCount;
import com.meilai.project.entity.media.*;
import com.meilai.project.mapper.business.building.BuildingAreaMapper;
import com.meilai.project.mapper.business.combo.GasStationComboMapper;
import com.meilai.project.mapper.business.media.MediaPointMapper;
import com.meilai.project.mapper.intentionScheme.IntentionSchemeMapper;
import com.meilai.project.mapper.media.*;
import com.meilai.project.service.business.contract.ContractApplyService;
import com.meilai.project.service.business.installOrder.WorkOrderInstallDetailService;
import com.meilai.project.service.business.installOrder.WorkOrderInstallDetailWorkerService;
import com.meilai.project.service.business.installOrder.WorkOrderService;
import com.meilai.project.service.business.installOrder.WorkerSubmitService;
import com.meilai.project.service.business.sale.BillSaleService;
import com.meilai.project.service.media.MediaPointChooseDaterangeComboCountService;
import com.meilai.project.service.media.MediaPointChooseDaterangeMediapointService;
import com.meilai.project.util.DistanceUtil;
import com.meilai.project.util.entity.PointRange;
import com.meilai.project.vo.business.intentionScheme.ComboVO;
import com.meilai.project.vo.business.intentionScheme.IntentionSchemeVO;
import com.meilai.project.vo.business.intentionScheme.PointProcessDateRangeVO;
import com.meilai.project.vo.business.media.MediaPointExtVO;
import com.meilai.project.vo.business.sale.BillSaleVO;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author xchen
 * @since 2022-03-05
 */
@Service
public class PointProcessServiceImpl implements PointProcessService {

    @Autowired
    private BuildingAreaMapper buildingAreaMapper;

    @Autowired
    private MediaPointMapper mediaPointMapper;

    @Autowired
    private MediaPointAbMapper mediaPointAbMapper;

    @Autowired
    private MediaPointOccupySnapshotMapper mediaPointOccupySnapshotMapper;

    @Autowired
    private IntentionSchemeMapper intentionSchemeMapper;

    @Autowired
    private MediaPointChooseMapper mediaPointChooseMapper;

    @Autowired
    private MediaPointChooseDaterangeMapper chooseDaterangeMapper;

    @Autowired
    private MediaPointChooseDaterangeMediapointService mediaPointChooseDaterangeMediapointService;

    @Autowired
    private MediaPointChooseDaterangeMediapointMapper chooseDaterangeMediapointMapper;

    @Autowired
    private MediaPointChooseDaterangeComboCountService mediaPointChooseDaterangeComboCountService;

    @Autowired
    private VGasStationComboRuleMediaTypeCountMapper comboRuleMediaTypeCountMapper;

    @Autowired
    private GasStationComboMapper gasStationComboMapper;

    @Autowired
    private WorkOrderInstallDetailService installDetailService;

    @Autowired
    private WorkOrderInstallDetailWorkerService installDetailWorkerService;

    @Autowired
    private WorkerSubmitService workerSubmitService;


    @Autowired
    private WorkOrderShootRequirementService workOrderShootRequirementService;
    @Autowired
    private ContractApplyService contractApplyService;
    @Autowired
    private BillSaleService billSaleService;
    @Autowired
    private WorkOrderService workOrderService;
    @Autowired
    private WorkOrderInstallDetailService workOrderInstallDetailService;

    //todo 之前存在了，要拷贝过来。
    @Override
    public void copyWorkOrder(Long newSaleId, Long oldWorkOrderId) {
        WorkOrder oldWorkOrder = workOrderService.getById(oldWorkOrderId);
        if (null == oldWorkOrder) {
            return;
        }
        oldWorkOrder.setBill_sale_id(newSaleId);
        oldWorkOrder.setId(null);
        oldWorkOrder.setMode(0);
        //通过老的工单，生成新的工单
        workOrderService.save(oldWorkOrder);
        Long newWorkOrderId = oldWorkOrder.getId();
        List<WorkOrderInstallDetailWorker> workOrderInstallDetailWorkers = installDetailWorkerService.listByWorkOrderId(oldWorkOrderId);
        Map<Integer, List<WorkOrderInstallDetailWorker>> detailIdMap = workOrderInstallDetailWorkers.stream()
                .collect(Collectors.groupingBy(WorkOrderInstallDetailWorker::getWork_order_install_detail_id));
        List<WorkOrderInstallDetail> workOrderInstallDetails = installDetailService.listByWorkOrderId(oldWorkOrderId);
        List<WorkOrderInstallDetailWorker> workerList = new ArrayList<>();
        for (WorkOrderInstallDetail workOrderInstallDetail : workOrderInstallDetails) {
            List<WorkOrderInstallDetailWorker> installDetailWorkers = detailIdMap.get(workOrderInstallDetail.getId().intValue());
            workOrderInstallDetail.setWork_order_id(Math.toIntExact(newWorkOrderId));
            workOrderInstallDetail.setId(null);
            installDetailService.save(workOrderInstallDetail);
            if (CollUtil.isNotEmpty(installDetailWorkers)) {
                installDetailWorkers.forEach(item -> {
                    item.setWork_order_install_detail_id(Math.toIntExact(workOrderInstallDetail.getId()));
                    item.setWork_order_id(Math.toIntExact(newWorkOrderId));
                });
                workerList.addAll(installDetailWorkers);
            }
        }
        installDetailWorkerService.saveBatch(workerList);
        List<WorkerSubmit> workerSubmits = workerSubmitService.listByWorkOrderId(oldWorkOrderId);
        for (WorkerSubmit workerSubmit : workerSubmits) {
            workerSubmit.setWork_order_id(newWorkOrderId);
            workerSubmit.setId(null);
        }
        workerSubmitService.saveBatch(workerSubmits);
    }

    @Override
    public Long newMonitorWorkOrder(BillSaleVO saleVO, List<Long> buildAreaIdList, LocalDate deadLine, List<Long> shootRequirementIdList) {
        //生成上刊工单
        WorkOrder workOrder = new WorkOrder();
        workOrder.setWork_order_type_id(8);
        workOrder.setBill_sale_id(saleVO.getId());
        workOrder.setName(workOrderName(saleVO));
        workOrder.setDeadline(deadLine.atStartOfDay().minusSeconds(1));
        workOrder.setStatus(1);
        workOrder.setMode(0);
        workOrder.setHand_closed(false);
        workOrderService.save(workOrder);
        int work_order_id = workOrder.getId().intValue();
        List<WorkOrderInstallDetail> installDetails = new ArrayList<>();
        for (Long buildAreaId : buildAreaIdList) {
            WorkOrderInstallDetail item = new WorkOrderInstallDetail();
            item.setBuilding_area_id(buildAreaId.intValue());
            item.setWork_order_id(work_order_id);
            installDetails.add(item);
        }
        workOrderInstallDetailService.saveBatch(installDetails);

        LambdaQueryWrapper<WorkOrderShootRequirement> deleteWrapper = new LambdaQueryWrapper<>();
        deleteWrapper.in(WorkOrderShootRequirement::getShoot_requirement_id, shootRequirementIdList);
        workOrderShootRequirementService.remove(deleteWrapper);

        List<WorkOrderShootRequirement> workOrderShootRequirements = shootRequirementIdList.stream().map(requirementId -> {
            WorkOrderShootRequirement workOrderShootRequirement = new WorkOrderShootRequirement();
            workOrderShootRequirement.setShoot_requirement_id(requirementId);
            workOrderShootRequirement.setWork_order_id((long) work_order_id);
            return workOrderShootRequirement;
        }).collect(Collectors.toList());
        workOrderShootRequirementService.saveBatch(workOrderShootRequirements);
        return workOrder.getId();
    }

    @Override
    public Long newUpWorkOrder(BillSaleVO saleVO, List<Long> buildAreaIdList) {
        //生成上刊工单
        WorkOrder workOrder = new WorkOrder();
        workOrder.setWork_order_type_id(2);
        workOrder.setBill_sale_id(saleVO.getId());
        workOrder.setName(workOrderName(saleVO));
        // todo 上刊工单截止时间为时间段开始时间顺延一周
        workOrder.setDeadline(saleVO.getDateRangeBeginDate().plusDays(7L).atStartOfDay());
        workOrder.setStatus(1);
        workOrder.setMode(0);
        workOrder.setHand_closed(false);
        workOrderService.save(workOrder);
        List<WorkOrderInstallDetail> installDetails = new ArrayList<>();
        for (Long buildAreaId : buildAreaIdList) {
            WorkOrderInstallDetail item = new WorkOrderInstallDetail();
            item.setBuilding_area_id(buildAreaId.intValue());
            item.setWork_order_id(workOrder.getId().intValue());
            installDetails.add(item);
        }
        workOrderInstallDetailService.saveBatch(installDetails);
        return workOrder.getId();
    }

    @Override
    public Long newDownWorkOrder(BillSaleVO saleVO, List<Long> buildAreaIdList) {
        //生成上刊工单
        WorkOrder workOrder = new WorkOrder();
        workOrder.setWork_order_type_id(3);
        workOrder.setBill_sale_id(saleVO.getId());
        workOrder.setName(workOrderName(saleVO));
        // todo 下刊工单截止时间为时间段结束时间顺延一周
        workOrder.setDeadline(saleVO.getDateRangeEndDate().plusDays(7L).atStartOfDay());
        workOrder.setStatus(1);
        workOrder.setMode(0);
        workOrder.setHand_closed(false);
        workOrderService.save(workOrder);
        List<WorkOrderInstallDetail> installDetails = new ArrayList<>();
        for (Long buildAreaId : buildAreaIdList) {
            WorkOrderInstallDetail item = new WorkOrderInstallDetail();
            item.setBuilding_area_id(buildAreaId.intValue());
            item.setWork_order_id(workOrder.getId().intValue());
            installDetails.add(item);
        }
        workOrderInstallDetailService.saveBatch(installDetails);
        return workOrder.getId();
    }

    @Override
    public Long newReplaceWorkOrder(BillSaleVO saleVO, List<Long> buildAreaIdList) {
        //生成上刊工单
        WorkOrder workOrder = new WorkOrder();
        workOrder.setWork_order_type_id(7);
        workOrder.setBill_sale_id(saleVO.getId());
        workOrder.setName(workOrderName(saleVO));
        workOrder.setDeadline(saleVO.getDateRangeEndDate().atStartOfDay());
        workOrder.setStatus(1);
        workOrder.setMode(0);
        workOrder.setHand_closed(false);
        workOrderService.save(workOrder);
        List<WorkOrderInstallDetail> installDetails = new ArrayList<>();
        for (Long buildAreaId : buildAreaIdList) {
            WorkOrderInstallDetail item = new WorkOrderInstallDetail();
            item.setBuilding_area_id(buildAreaId.intValue());
            item.setWork_order_id(workOrder.getId().intValue());
            installDetails.add(item);
        }
        workOrderInstallDetailService.saveBatch(installDetails);
        return workOrder.getId();
    }

    private String workOrderName(BillSaleVO saleVO) {
        IntentionSchemeVO intentionSchemeVO = intentionSchemeMapper.getOneById(saleVO.getIntention_scheme_id());
        return intentionSchemeVO.getPlan_name();
    }

    @Override
    public List<MediaPointChooseDaterangeMediapoint> listByChooseId(Long chooseId) {
        return mediaPointChooseDaterangeMediapointService.listByChooseId(chooseId);
    }

    @Override
    public List<ComboVO> getComboCountList() {
        List<VGasStationComboRuleMediaTypeCount> comboList = comboRuleMediaTypeCountMapper.selectList(null);
        return ComboVO.parse(comboList);
    }

    @Override
    public Map<Long, String> getRuleNameMap() {
        Map<Long, String> ruleNameMap = new HashMap<>();
        List<ComboVO> comboVOList = getComboCountList();
        for (ComboVO comboVO : comboVOList) {
            for (ComboVO.RuleVO ruleVO : comboVO.getRuleList()) {
                ruleNameMap.put(ruleVO.getRule_id(), ruleVO.getRule_name());
            }
        }
        return ruleNameMap;
    }

    @Override
    public Map<Long, GasStationCombo> getComboMap() {
        List<GasStationCombo> gasStationCombos = gasStationComboMapper.listAll();
        Map<Long, GasStationCombo> result = new HashMap<>();
        for (GasStationCombo gasStationCombo : gasStationCombos) {
            result.put(gasStationCombo.getId(), gasStationCombo);
        }
        return result;
    }

    @Override
    public PointProcessLockedMediaPointDTO checkOccupyMediaPointList(
            Long chooseId, List<PointProcessDateRangeDTO> dataList) {
        return checkOccupyMediaPointList(CollUtil.newArrayList(chooseId), dataList);
    }

    @Override
    public PointProcessLockedMediaPointDTO checkOccupyMediaPointList(List<Long> chooseIdList, List<PointProcessDateRangeDTO> dataList) {
        chooseIdList = chooseIdList.stream().filter(Objects::nonNull).collect(Collectors.toList());
        for (PointProcessDateRangeDTO pointProcessDateRangeDTO : dataList) {
            List<MediaPointSimpleVO> pointList = pointProcessDateRangeDTO.getPointList();
            List<Long> mediaPointIds = pointList.stream().map(MediaPointSimpleVO::getId).collect(Collectors.toList());
            List<Long> lockedIds = mediaPointOccupySnapshotMapper.occupyIdsExcludeChooseIds(chooseIdList, pointProcessDateRangeDTO, mediaPointIds);
            if (CollUtil.isNotEmpty(lockedIds)) {
                List<String> lockPlanNameList = mediaPointOccupySnapshotMapper.queryLockSchemeNameListByMediaIds(lockedIds);
                Set<Long> lockIdSet = new HashSet<>(lockedIds);
                List<MediaPointSimpleVO> lockedMediaPointList = pointList.stream()
                        .filter(item -> lockIdSet.contains(item.getId())).collect(Collectors.toList());
                List<Building2PointType2PointVO> building2PointType2PointVOList = assembleLockBuildingMediaPointInfo(lockedMediaPointList);
                PointProcessLockedMediaPointDTO lockedMediaPointDTO = new PointProcessLockedMediaPointDTO();
                lockedMediaPointDTO.setDateBegin(pointProcessDateRangeDTO.getBeginDate());
                lockedMediaPointDTO.setDateEnd(pointProcessDateRangeDTO.getEndDate());
                lockedMediaPointDTO.setIndex(pointProcessDateRangeDTO.getIndex());
                lockedMediaPointDTO.setRepeatMediaPointList(building2PointType2PointVOList);
                lockedMediaPointDTO.setConflictSchemeNameList(lockPlanNameList);
                return lockedMediaPointDTO;
            }
        }
        return null;
    }

    private List<Building2PointType2PointVO> assembleLockBuildingMediaPointInfo(List<MediaPointSimpleVO> lockedMediaPointList) {
        List<Long> buildIdList = lockedMediaPointList.stream().map(MediaPointSimpleVO::getBuilding_area_id).collect(Collectors.toList());
        List<BuildingArea> buildingAreaList = buildingAreaMapper.selectBatchIds(buildIdList);
        Map<Long, List<MediaPointSimpleVO>> build2PointMap = lockedMediaPointList.stream()
                .collect(Collectors.groupingBy(MediaPointSimpleVO::getBuilding_area_id));
        List<Building2PointType2PointVO> building2PointType2PointVOList = new ArrayList<>();
        for (BuildingArea buildingArea : buildingAreaList) {
            Building2PointType2PointVO buildingPointVO = new Building2PointType2PointVO();
            buildingPointVO.setId(buildingArea.getId());
            buildingPointVO.setName(buildingArea.getName());
            buildingPointVO.setAddress(buildingArea.getAddress());
            List<MediaPointSimpleVO> mediaPointVOList = build2PointMap.get(buildingArea.getId());
            Map<Long, List<MediaPointSimpleVO>> mediaType2PointMap = mediaPointVOList.stream().collect(Collectors.groupingBy(MediaPointSimpleVO::getMedia_type_id));
            buildingPointVO.setMedia_point_list_map(mediaType2PointMap);
            building2PointType2PointVOList.add(buildingPointVO);
        }
        return building2PointType2PointVOList;
    }

    public PointProcessDateRangeLockedDTO checkRangeDateLockedPointList(List<PointProcessDateRangeDTO> dataList) {
        if (dataList.size() > 1) {
            //找到所有重叠的时间区间
            List<Pair<PointProcessDateRangeDTO>> pairs = new ArrayList<>();
            for (int i = 0; i < dataList.size() - 1; i++) {
                PointProcessDateRangeDTO item1 = dataList.get(i);
                for (int j = i + 1; j < dataList.size(); j++) {
                    PointProcessDateRangeDTO item2 = dataList.get(j);
                    if (item1.isOverlap(item2)) {
                        Pair<PointProcessDateRangeDTO> pair = new Pair<>();
                        pair.setOne(item1);
                        pair.setTow(item2);
                        pairs.add(pair);
                    }
                }
            }
            for (Pair<PointProcessDateRangeDTO> pair : pairs) {
                PointProcessDateRangeDTO one = pair.getOne();
                PointProcessDateRangeDTO tow = pair.getTow();
                List<MediaPointSimpleVO> onePointList = one.getPointList();
                List<MediaPointSimpleVO> towPointList = tow.getPointList();
                Collection<MediaPointSimpleVO> intersection = CollUtil.intersection(onePointList, towPointList);
                if (intersection.size() > 0) {
                    PointProcessDateRangeLockedDTO lockedDTO = new PointProcessDateRangeLockedDTO();
                    lockedDTO.setDate1Begin(one.getBeginDate());
                    lockedDTO.setDate1End(one.getEndDate());
                    lockedDTO.setDate2Begin(tow.getBeginDate());
                    lockedDTO.setDate2End(tow.getEndDate());
                    List<Building2PointType2PointVO> building2PointType2PointVOList = assembleLockBuildingMediaPointInfo(new ArrayList<>(intersection));
                    lockedDTO.setRepeatMediaPointList(building2PointType2PointVOList);
                    //返回哪两个区间,哪些点位是重复的
                    return lockedDTO;
                }
            }
        }
        return null;
    }

    @Override
    public List<BuildingPointVO> search(PointProcessSearchDTO searchDTO) {
        //查询楼盘列表（通过：选择的楼盘、半径、禁投行业三个条件筛选）
        List<BuildingArea> filterBuildingAreaList = getFilterBuildingAreaList(searchDTO);

        //根据点位类型，楼层，A,B类型，筛选出来的buildId，禁投行业，区域，所有未锁定的point_id 作为filter,
        //所输入的地址半径作为查询条件，查出所有的点位(这时候是所有未锁定的点位)
        List<MediaPointExt> pointList = getMeetConditionPoint(searchDTO, filterBuildingAreaList);

        //如果选择的是A+B型，则必须是一块设备都空时才可以，通过media_point_ab来对结果进行过滤
        Map<Long, Long> ABPointMap = new HashMap<>();
        if (CollUtil.isNotEmpty(pointList)) {
            //筛选出符合条件的AB point
            List<MediaPointAB> mediaPointABS = mediaPointMapper.listAbNotOccupyABPoint(searchDTO, pointList);
            Set<Long> pointIdSet = new HashSet<>();
            for (MediaPointAB mediaPointAB : mediaPointABS) {
                pointIdSet.add(mediaPointAB.getA_point_id());
                pointIdSet.add(mediaPointAB.getB_point_id());
                //保存A,B面的关系
                ABPointMap.put(mediaPointAB.getA_point_id(), mediaPointAB.getB_point_id());
                ABPointMap.put(mediaPointAB.getB_point_id(), mediaPointAB.getA_point_id());
            }
            pointList = pointList.stream().filter(item -> {
                if (!AbType.AB.equals(searchDTO.getAbType())) {
                    return true;
                }
                if (pointIdSet.contains(item.getId())) {
                    return true;
                }
                List<Long> must_include_media_id_list = searchDTO.getMust_include_media_id_list();
                if (CollUtil.isNotEmpty(must_include_media_id_list)) {
                    return must_include_media_id_list.contains(item.getId());
                }
                return false;
            }).collect(Collectors.toList());
        }

        //返回结果(building - type - count - detail)
        List<BuildingPointVO> result = new ArrayList<>();
        if (CollUtil.isEmpty(pointList)) {
            return result;
        }

        return assembleBuildingPointVO(pointList, searchDTO.getPointTypeIdList(), ABPointMap, null);
    }

    @Override
    public List<BuildingPointVO> searchAll(PointProcessSearchDTO searchDTO) {
        //查询楼盘列表（通过：选择的楼盘、半径、禁投行业三个条件筛选）
        List<BuildingArea> filterBuildingAreaList = getFilterBuildingAreaList(searchDTO);

        //根据点位类型，楼层，A,B类型，筛选出来的buildId，禁投行业，区域，所有未锁定的point_id 作为filter,
        //所输入的地址半径作为查询条件，查出所有的点位(这时候是所有未锁定的点位)
        List<MediaPointExtVO> pointList = getMeetConditionPoint2(searchDTO, filterBuildingAreaList);
        List<MediaPointExt> collect = pointList.stream().map(item -> {
            MediaPointExt pointExt = new MediaPointExt();
            BeanUtils.copyProperties(item, item);
            return pointExt;
        }).collect(Collectors.toList());
        //如果选择的是A+B型，则必须是一块设备都空时才可以，通过media_point_ab来对结果进行过滤
        Map<Long, Long> ABPointMap = new HashMap<>();
        if (CollUtil.isNotEmpty(pointList)) {
            //筛选出符合条件的AB point
            List<MediaPointAB> mediaPointABS = mediaPointMapper.listAbNotOccupyABPoint(searchDTO, collect);
            Set<Long> pointIdSet = new HashSet<>();
            for (MediaPointAB mediaPointAB : mediaPointABS) {
                pointIdSet.add(mediaPointAB.getA_point_id());
                pointIdSet.add(mediaPointAB.getB_point_id());
                //保存A,B面的关系
                ABPointMap.put(mediaPointAB.getA_point_id(), mediaPointAB.getB_point_id());
                ABPointMap.put(mediaPointAB.getB_point_id(), mediaPointAB.getA_point_id());
            }
            pointList = pointList.stream().filter(item -> {
                if (!AbType.AB.equals(searchDTO.getAbType())) {
                    return true;
                }
                if (pointIdSet.contains(item.getId())) {
                    return true;
                }
                List<Long> must_include_media_id_list = searchDTO.getMust_include_media_id_list();
                if (CollUtil.isNotEmpty(must_include_media_id_list)) {
                    return must_include_media_id_list.contains(item.getId());
                }
                return false;
            }).collect(Collectors.toList());
        }

        //返回结果(building - type - count - detail)
        List<BuildingPointVO> result = new ArrayList<>();
        if (CollUtil.isEmpty(pointList)) {
            return result;
        }

        return assembleBuildingPointVO(pointList, searchDTO.getPointTypeIdList(), ABPointMap, null);
    }

    @Override
    public Long newUpWorkOrder(BillMarketDetailVO saleVO, List<Long> buildAreaIdList) {
        //生成上刊工单
        WorkOrder workOrder = new WorkOrder();
        workOrder.setWork_order_type_id(2);
        workOrder.setBill_sale_id(saleVO.getBill_sale_id());
        workOrder.setBill_market_detail_id(saleVO.getId());
        workOrder.setName(workOrderName(saleVO));
        // todo 上刊工单截止时间为时间段开始时间顺延一周
        workOrder.setDeadline(saleVO.getDateRangeBeginDate().plusDays(7L).atStartOfDay());
        workOrder.setStatus(1);
        workOrder.setMode(0);
        workOrder.setHand_closed(false);
        workOrderService.save(workOrder);
        List<WorkOrderInstallDetail> installDetails = new ArrayList<>();
        for (Long buildAreaId : buildAreaIdList) {
            WorkOrderInstallDetail item = new WorkOrderInstallDetail();
            item.setBuilding_area_id(buildAreaId.intValue());
            item.setWork_order_id(workOrder.getId().intValue());
            installDetails.add(item);
        }
        workOrderInstallDetailService.saveBatch(installDetails);
        return workOrder.getId();
    }

    @Override
    public Long newDownWorkOrder(BillMarketDetailVO saleVO, List<Long> buildAreaIdList) {
        //生成上刊工单
        WorkOrder workOrder = new WorkOrder();
        workOrder.setWork_order_type_id(3);
        workOrder.setBill_sale_id(saleVO.getBill_sale_id());
        workOrder.setBill_market_detail_id(saleVO.getId());
        workOrder.setName(workOrderName(saleVO));
        // todo 下刊工单截止时间为时间段结束时间顺延一周
        workOrder.setDeadline(saleVO.getDateRangeEndDate().plusDays(7L).atStartOfDay());
        workOrder.setStatus(1);
        workOrder.setMode(0);
        workOrder.setHand_closed(false);
        workOrderService.save(workOrder);
        List<WorkOrderInstallDetail> installDetails = new ArrayList<>();
        for (Long buildAreaId : buildAreaIdList) {
            WorkOrderInstallDetail item = new WorkOrderInstallDetail();
            item.setBuilding_area_id(buildAreaId.intValue());
            item.setWork_order_id(workOrder.getId().intValue());
            installDetails.add(item);
        }
        workOrderInstallDetailService.saveBatch(installDetails);
        return workOrder.getId();
    }

    @Override
    public Long newReplaceWorkOrder(BillMarketDetailVO saleVO, List<Long> buildAreaIdList) {
        //生成上刊工单
        WorkOrder workOrder = new WorkOrder();
        workOrder.setWork_order_type_id(7);
        workOrder.setBill_sale_id(saleVO.getBill_sale_id());
        workOrder.setBill_market_detail_id(saleVO.getId());
        workOrder.setName(workOrderName(saleVO));
        workOrder.setDeadline(saleVO.getDateRangeEndDate().atStartOfDay());
        workOrder.setStatus(1);
        workOrder.setMode(0);
        workOrder.setHand_closed(false);
        workOrderService.save(workOrder);
        List<WorkOrderInstallDetail> installDetails = new ArrayList<>();
        for (Long buildAreaId : buildAreaIdList) {
            WorkOrderInstallDetail item = new WorkOrderInstallDetail();
            item.setBuilding_area_id(buildAreaId.intValue());
            item.setWork_order_id(workOrder.getId().intValue());
            installDetails.add(item);
        }
        workOrderInstallDetailService.saveBatch(installDetails);
        return workOrder.getId();
    }

    @Override
    public int releaseLock(Long choose_id) {
        return mediaPointOccupySnapshotMapper.deleteByChooseId(choose_id);
    }

    @Override
    public int releaseLockAndBackUp(List<Long> chooseIdList) {
        for (Long chooseId : chooseIdList) {
            intentionSchemeMapper.updateLockStatus(chooseId, 1);
        }
        mediaPointOccupySnapshotMapper.backup(chooseIdList);
        mediaPointOccupySnapshotMapper.deleteByChooseIdList(chooseIdList);
        return 0;
    }

    @Override
    public int rollback(List<Long> chooseIdList) {
        for (Long chooseId : chooseIdList) {
            intentionSchemeMapper.updateLockStatus(chooseId, 2);
        }
        mediaPointOccupySnapshotMapper.rollback(chooseIdList);
        mediaPointOccupySnapshotMapper.deleteBackUpByChooseIdList(chooseIdList);
        return 0;
    }

    @Override
    public Long getSaleChooseId(Long saleId) {
        BillSaleVO sale = billSaleService.getOneById(saleId);
        return sale.getChoose_id();
    }

    @Override
    public void hangUpByChooseIds(List<Long> chooseIdList) {
//		contractApplyService.hangUp(chooseIdList);
        billSaleService.hangUpByChooseIds(chooseIdList);
        workOrderService.hangUpByChooseIds(chooseIdList);
    }

    @Override
    public void cancelHangUpByChooseIds(List<Long> chooseIdList) {
//		contractApplyService.cancelHangUp(chooseIdList);
        billSaleService.cancelHangUpByChooseIds(chooseIdList);
        workOrderService.cancelHangUpByChooseIds(chooseIdList);
    }

    @Override
    public void invalidByChooseIds(List<Long> chooseIdList) {
//		contractApplyService.invalidByChooseIds(chooseIdList);
        billSaleService.invalidByChooseIds(chooseIdList);
        workOrderService.invalidByChooseIds(chooseIdList);
    }

    @Override
    public void invalidIntentionScheme(Long contractId) {

    }


    @Override
    public Long saveChoose(Integer chooseType, Long choose_id, List<PointProcessDateRangeDTO> dataList) {
        //保存从表
        Long dateRangeId = null;
        if (null == choose_id) {
            //如果choose没有，则生成一个新的
            MediaPointChoose choose = new MediaPointChoose();
            choose.setType(chooseType);
            mediaPointChooseMapper.insert(choose);
            choose_id = choose.getId();
        } else {
            //清理子表数据
            //chooseDaterangeMapper.deleteByChooseId(choose_id);
            dateRangeId = chooseDaterangeMapper.queryDaterangeIdByChooseId(choose_id);
            chooseDaterangeMediapointMapper.deleteByChooseId(choose_id);
            mediaPointChooseDaterangeComboCountService.deleteByChooseId(choose_id);
        }

        for (PointProcessDateRangeDTO dateRangeDTO : dataList) {
            MediaPointChooseDaterange pointChooseDaterange = new MediaPointChooseDaterange();
            pointChooseDaterange.setId(dateRangeId);
            pointChooseDaterange.setChoose_id(choose_id);
            pointChooseDaterange.setStart_at(dateRangeDTO.getBeginDate().atStartOfDay());
            pointChooseDaterange.setEnd_at(dateRangeDTO.getEndDate().atStartOfDay());
            if (null == dateRangeId) {
                chooseDaterangeMapper.insert(pointChooseDaterange);
            } else {
                chooseDaterangeMapper.updateById(pointChooseDaterange);
            }
            Long daterangeId = pointChooseDaterange.getId();

            List<MediaPointChooseDaterangeMediapoint> items = new ArrayList<>();
            for (MediaPointSimpleVO mediaPointVO : dateRangeDTO.getPointList()) {
                MediaPointChooseDaterangeMediapoint item = new MediaPointChooseDaterangeMediapoint();
                item.setChoose_id(choose_id);
                item.setDaterange_id(daterangeId);
                item.setMedia_point_id(mediaPointVO.getId());
                item.setMedia_type_spec_price_id(mediaPointVO.getMedia_type_spec_price_id());
                items.add(item);
            }
            mediaPointChooseDaterangeMediapointService.saveBatch(items);

            List<PointProcessDateRangeDTO.ComboInfo> comboInfoList = dateRangeDTO.getComboInfoList();
            if (CollUtil.isNotEmpty(comboInfoList)) {
                List<MediaPointChooseDaterangeComboCount> comboCountList = new ArrayList<>();
                for (PointProcessDateRangeDTO.ComboInfo comboInfo : comboInfoList) {
                    MediaPointChooseDaterangeComboCount comboCount = new MediaPointChooseDaterangeComboCount();
                    comboCount.setChoose_id(choose_id);
                    comboCount.setDaterange_id(daterangeId);
                    comboCount.setBuilding_area_id(comboInfo.getBuilding_area_id());
                    comboCount.setCombo_id(comboInfo.getCombo_id());
                    comboCount.setRule_id(comboInfo.getRule_id());
                    comboCount.setCount(comboInfo.getCount());
                    comboCountList.add(comboCount);
                }
                mediaPointChooseDaterangeComboCountService.saveBatch(comboCountList);
            }
        }
        return choose_id;
    }

    @Override
    public Long copyChooseAndTransferLockPoint(Integer newChooseType, List<Long> oldChooseIdList) {
        //生成新的chooseId
        MediaPointChoose choose = new MediaPointChoose();
        choose.setType(newChooseType);
        mediaPointChooseMapper.insert(choose);
        Long new_choose_id = choose.getId();
        for (Long oldChooseId : oldChooseIdList) {
            List<MediaPointChooseDaterange> mediaPointChooseDateRanges =
                    chooseDaterangeMapper.listByChooseId(oldChooseId);
            for (MediaPointChooseDaterange mediaPointChooseDateRange : mediaPointChooseDateRanges) {
                extracted(new_choose_id, oldChooseId, mediaPointChooseDateRange);
            }
        }
        return new_choose_id;
    }

    private Long extracted(Long new_choose_id, Long oldChooseId, MediaPointChooseDaterange mediaPointChooseDateRange) {
        Long oldDateRangeId = mediaPointChooseDateRange.getId();
        mediaPointChooseDateRange.setChoose_id(new_choose_id);
        mediaPointChooseDateRange.setId(null);
        chooseDaterangeMapper.insert(mediaPointChooseDateRange);
        Long newDateRangeId = mediaPointChooseDateRange.getId();
        chooseDaterangeMediapointMapper.copyOldToNew(new_choose_id, newDateRangeId, oldChooseId, oldDateRangeId);
        mediaPointChooseDaterangeComboCountService.copyOldToNew(new_choose_id, newDateRangeId, oldChooseId, oldDateRangeId);
        //占用点位到合同上
        mediaPointOccupySnapshotMapper.copyOldToNew(new_choose_id, newDateRangeId, oldChooseId, oldDateRangeId);
        mediaPointOccupySnapshotMapper.deleteByChooseIdAndDateRangeId(oldChooseId, oldDateRangeId);
        return newDateRangeId;
    }

    @Override
    public Long[] copyChooseAndTransferLockPoint(Integer newChooseType, Long oldChooseId, Long oldRangeId) {
        //生成新的chooseId
        MediaPointChoose choose = new MediaPointChoose();
        choose.setType(newChooseType);
        mediaPointChooseMapper.insert(choose);
        Long new_choose_id = choose.getId();
        List<MediaPointChooseDaterange> mediaPointChooseDateRanges =
                chooseDaterangeMapper.listByChooseId(oldChooseId);
        Long dateRangeId = null;
        for (MediaPointChooseDaterange mediaPointChooseDateRange : mediaPointChooseDateRanges) {
            Long oldDateRangeId = mediaPointChooseDateRange.getId();
            if (oldRangeId == null || !oldRangeId.equals(oldDateRangeId)) {
                continue;
            }
            dateRangeId = extracted(new_choose_id, oldChooseId, mediaPointChooseDateRange);
            break;
        }
        return new Long[]{new_choose_id, dateRangeId};
    }

    @Override
    public int lock(Long choose_id) {
        //保存到锁定表
        mediaPointOccupySnapshotMapper.deleteByChooseId(choose_id);
        return mediaPointOccupySnapshotMapper.saveByChooseId(choose_id);
    }

    @Override
    public List<PointProcessDateRangeVO> queryChooseDateRangeVOList(Long choose_id) {
        List<PointProcessDateRangeVO> result = new ArrayList<>();

        List<MediaPointAB> mediaPointABS = mediaPointAbMapper.listAll();
        Map<Long, Long> ABMediaPointMap = new HashMap<>();
        for (MediaPointAB mediaPointAB : mediaPointABS) {
            ABMediaPointMap.put(mediaPointAB.getA_point_id(), mediaPointAB.getB_point_id());
            ABMediaPointMap.put(mediaPointAB.getB_point_id(), mediaPointAB.getA_point_id());
        }

        List<MediaPointExt> daterangeMediapointList =
                chooseDaterangeMediapointMapper.listMediaPointByChooseId(choose_id);

        Map<Long, List<MediaPointExt>> date2MediaPointMap = daterangeMediapointList.stream()
                .collect(Collectors.groupingBy(MediaPointExt::getDaterange_id));
        List<MediaPointChooseDaterange> mediaPointChooseDateranges = chooseDaterangeMapper.listByChooseId(choose_id);

        Map<Long, List<BuildingPointVO.ComboCountInfo>> buildingComboInfoMap = assembleBuildingComboInfo(choose_id);
        for (MediaPointChooseDaterange mediaPointChooseDaterange : mediaPointChooseDateranges) {
            PointProcessDateRangeVO dateRangeVO = new PointProcessDateRangeVO();
            dateRangeVO.setId(mediaPointChooseDaterange.getId());
            dateRangeVO.setBeginDate(mediaPointChooseDaterange.getStart_at().toLocalDate());
            dateRangeVO.setEndDate(mediaPointChooseDaterange.getEnd_at().toLocalDate());
            List<Long> pointTypeIdList = daterangeMediapointList.stream()
                    .filter(item -> item.getDaterange_id().equals(mediaPointChooseDaterange.getId()))
                    .map(MediaPointExt::getMedia_type_id)
                    .collect(Collectors.toList());
            List<BuildingPointVO> buildingPointVOS = assembleBuildingPointVO(date2MediaPointMap.getOrDefault(mediaPointChooseDaterange.getId(), Collections.emptyList()),
                    pointTypeIdList, ABMediaPointMap, buildingComboInfoMap);
            dateRangeVO.setBuildingPointVOList(buildingPointVOS);
            result.add(dateRangeVO);
        }

        return result;
    }

    @Override
    public List<PointProcessDateRangeVO> queryChooseDateRangeVOList(Long choose_id, Long dateRangeId) {
        List<PointProcessDateRangeVO> pointProcessDateRangeVOList = queryChooseDateRangeVOList(choose_id);
        return pointProcessDateRangeVOList.stream().filter(item -> item.getId().equals(dateRangeId))
                .collect(Collectors.toList());
    }

    public Map<Long, List<BuildingPointVO.ComboCountInfo>> assembleBuildingComboInfo(Long chooseId) {
        Map<Long, List<BuildingPointVO.ComboCountInfo>> result = new HashMap<>();
        List<MediaPointChooseDaterangeComboCount> comboCountList = mediaPointChooseDaterangeComboCountService.listByChooseId(chooseId);
        if (CollUtil.isEmpty(comboCountList)) {
            return result;
        }
        Map<Long, String> ruleNameMap = getRuleNameMap();
        Map<Long, GasStationCombo> comboInfoMap = getComboMap();
        Map<Long, List<MediaPointChooseDaterangeComboCount>> buildingMap = comboCountList.stream().collect(Collectors.groupingBy(MediaPointChooseDaterangeComboCount::getBuilding_area_id));

        for (Map.Entry<Long, List<MediaPointChooseDaterangeComboCount>> buildingMapEntry : buildingMap.entrySet()) {
            List<BuildingPointVO.ComboCountInfo> resultItem = new ArrayList<>();
            result.put(buildingMapEntry.getKey(), resultItem);
            List<MediaPointChooseDaterangeComboCount> item = buildingMapEntry.getValue();
            Map<Long, List<MediaPointChooseDaterangeComboCount>> comboMap = item.stream().collect(Collectors.groupingBy(MediaPointChooseDaterangeComboCount::getCombo_id));
            for (Map.Entry<Long, List<MediaPointChooseDaterangeComboCount>> comboMapEntry : comboMap.entrySet()) {
                BuildingPointVO.ComboCountInfo comboCountInfo = new BuildingPointVO.ComboCountInfo();
                Long key = comboMapEntry.getKey();
                comboCountInfo.setComboId(key);
                GasStationCombo gasStationCombo = comboInfoMap.get(key);
                if (null != gasStationCombo) {
                    comboCountInfo.setComboName(gasStationCombo.getName());
                    comboCountInfo.setCode(gasStationCombo.getCode());
                }

                resultItem.add(comboCountInfo);
                List<BuildingPointVO.RuleCountInfo> ruleCountInfoList = new ArrayList<>();
                comboCountInfo.setRuleCountList(ruleCountInfoList);
                Map<Long, List<MediaPointChooseDaterangeComboCount>> ruleMap = comboMapEntry.getValue().stream().collect(Collectors.groupingBy(MediaPointChooseDaterangeComboCount::getRule_id));
                for (Map.Entry<Long, List<MediaPointChooseDaterangeComboCount>> ruleMapEntry : ruleMap.entrySet()) {
                    BuildingPointVO.RuleCountInfo ruleCountInfo = new BuildingPointVO.RuleCountInfo();
                    ruleCountInfo.setRuleId(ruleMapEntry.getKey());
                    ruleCountInfo.setRuleName(ruleNameMap.get(ruleMapEntry.getKey()));
                    ruleCountInfo.setCount(ruleMapEntry.getValue().get(0).getCount().intValue());
                    ruleCountInfoList.add(ruleCountInfo);
                }
            }
        }
        return result;
    }

    public List<BuildingPointVO> assembleBuildingPointVO(
            List<? extends MediaPoint> pointList, List<Long> pointTypeIdList,
            Map<Long, Long> ABPointMap, Map<Long, List<BuildingPointVO.ComboCountInfo>> buildingComboInfoMap) {
        List<BuildingPointVO> result = new ArrayList<>();
        Map<Long, List<MediaPoint>> building2PointListMap =
                pointList.stream().collect(Collectors.groupingBy(MediaPoint::getBuilding_area_id));
        List<Long> buildIdList = new ArrayList<>(building2PointListMap.keySet());

        if(CollectionUtil.isNotEmpty(buildIdList)){
            List<BuildingArea> buildingAreaList = buildingAreaMapper.selectBatchIds(buildIdList);
            for (BuildingArea buildingArea : buildingAreaList) {
                BuildingPointVO item = new BuildingPointVO();
                item.setId(buildingArea.getId());
                item.setName(buildingArea.getName());
                item.setAddress(buildingArea.getAddress());
                item.setArea(buildingArea.getArea());
                if (null != buildingComboInfoMap) {
                    item.setComboCountInfoList(buildingComboInfoMap.get(buildingArea.getId()));
                }

                //包含的mediaPoints
                List<MediaPoint> mediaPoints = building2PointListMap.get(buildingArea.getId());
                Map<Long, List<MediaPoint>> pointTypeMap = mediaPoints.stream().filter(mp -> null != mp.getMedia_type_id()).collect(Collectors.groupingBy(MediaPoint::getMedia_type_id));
                //创建这个building下的各类型的点位信息
                Map<Long, PointTypeCountVO> pointTypeCountVOMap = new LinkedHashMap<>();
                item.setPointTypeCountVOMap(pointTypeCountVOMap);
                for (Long pointTypeId : pointTypeIdList) {
                    PointTypeCountVO itemVO = new PointTypeCountVO();
                    itemVO.setPoint_type_id(pointTypeId);
                    pointTypeCountVOMap.put(pointTypeId, itemVO);

                    //创建这个building下这个point_type的media_point列表
                    List<MediaPointSimpleVO> mediaPointSimpleVOList = new ArrayList<>();
                    itemVO.setMediaPointList(mediaPointSimpleVOList);
                    List<MediaPoint> subMediaList = pointTypeMap.get(pointTypeId);
                    if (CollUtil.isEmpty(subMediaList)) {
                        itemVO.setPoint_type_count(0);
                    } else {
                        for (MediaPoint mediaPoint : subMediaList) {
                            MediaPointSimpleVO mediaPointSimpleVO = MediaPointSimpleVO.parseFrom(mediaPoint);
                            mediaPointSimpleVO.setPair_media_id(ABPointMap.get(mediaPoint.getId()));
                            mediaPointSimpleVOList.add(mediaPointSimpleVO);
                        }
                        itemVO.setPoint_type_count(mediaPointSimpleVOList.size());
                    }
                    mediaPointSimpleVOList.sort(Comparator.comparing(MediaPointSimpleVO::getBuilding_number));
                }
                result.add(item);
            }
        }
        return result;
    }

    private List<MediaPointExt> getMeetConditionPoint(
            PointProcessSearchDTO searchDTO, List<BuildingArea> filterBuildingAreaList) {
        List<PointRange> pointRanges = new ArrayList<>();
        if (CollUtil.isNotEmpty(searchDTO.getPositionLocationList()) && null != searchDTO.getPositionRadius()) {
            for (LocationInfo locationInfo : searchDTO.getPositionLocationList()) {
                PointRange aroundRange = DistanceUtil.getAroundRange(locationInfo.getLat(),
                        locationInfo.getLng(), searchDTO.getPositionRadius());
                pointRanges.add(aroundRange);
            }
        }
        List<MediaPointExt> mediaPoints = mediaPointMapper.listByCondition(searchDTO, pointRanges, filterBuildingAreaList);
        //判断是否包含必须包含的点
        if (CollUtil.isEmpty(searchDTO.getMust_include_media_id_list())) {
            return mediaPoints;
        }
        mediaPoints.forEach(item -> {
            if (item.getMedia_type_id() == 11) {
                item.setMedia_type_spec_price_id(null);
            }
        });
        List<MediaPointExt> mustIncludeMediaPoints = mediaPointMapper.listByBatchIds(searchDTO.getMust_include_media_id_list());
        if (CollUtil.isEmpty(mustIncludeMediaPoints)) {
            return mediaPoints;
        }
        Set<Long> includeIdSet = mediaPoints.stream().map(MediaPoint::getId).collect(Collectors.toSet());
        for (MediaPointExt mustIncludeMediaPoint : mustIncludeMediaPoints) {
            if (!includeIdSet.contains(mustIncludeMediaPoint.getId())) {
                mediaPoints.add(mustIncludeMediaPoint);
            }
        }
        return mediaPoints;
    }

    // todo 不查询点位占用
    private List<MediaPointExtVO> getMeetConditionPoint2(
            PointProcessSearchDTO searchDTO, List<BuildingArea> filterBuildingAreaList) {
        List<PointRange> pointRanges = new ArrayList<>();
        if (CollUtil.isNotEmpty(searchDTO.getPositionLocationList()) && null != searchDTO.getPositionRadius()) {
            for (LocationInfo locationInfo : searchDTO.getPositionLocationList()) {
                PointRange aroundRange = DistanceUtil.getAroundRange(locationInfo.getLat(),
                        locationInfo.getLng(), searchDTO.getPositionRadius());
                pointRanges.add(aroundRange);
            }
        }
        List<MediaPointExtVO> mediaPoints = mediaPointMapper.listByCondition2(searchDTO, pointRanges, filterBuildingAreaList);
        //判断是否包含必须包含的点
        if (CollUtil.isEmpty(searchDTO.getMust_include_media_id_list())) {
            return mediaPoints;
        }
        mediaPoints.forEach(item -> {
            if (item.getMedia_type_id() == 11) {
                item.setMedia_type_spec_price_id(null);
            }
        });
//        List<MediaPointExt> mustIncludeMediaPoints = mediaPointMapper.listByBatchIds(searchDTO.getMust_include_media_id_list());
//        if (CollUtil.isEmpty(mustIncludeMediaPoints)) {
//            return mediaPoints;
//        }
//        Set<Long> includeIdSet = mediaPoints.stream().map(MediaPoint::getId).collect(Collectors.toSet());
//        for (MediaPointExt mustIncludeMediaPoint : mustIncludeMediaPoints) {
//            if (!includeIdSet.contains(mustIncludeMediaPoint.getId())) {
//                mediaPoints.add(mustIncludeMediaPoint);
//            }
//        }
        return mediaPoints;
    }

    private List<BuildingArea> getFilterBuildingAreaList(PointProcessSearchDTO searchDTO) {
        List<BuildingInfo> buildingList = searchDTO.getBuildingList();
        List<BuildingArea> buildFilterList = new ArrayList<>();
        if (CollUtil.isEmpty(buildingList)) {
            return buildFilterList;
        }
        for (BuildingInfo buildingInfo : buildingList) {
            buildFilterList.add(buildingInfo.to());
        }
        if (searchDTO.getBuildingRadiation()) {
            Integer buildingRadius = searchDTO.getBuildingRadius();
            List<PointRange> pointRanges = new ArrayList<>();
            for (BuildingInfo buildingInfo : buildingList) {
                PointRange aroundRange = DistanceUtil.getAroundRange(buildingInfo.getLat().doubleValue(),
                        buildingInfo.getLng().doubleValue(), buildingRadius);
                pointRanges.add(aroundRange);
                buildFilterList.add(buildingInfo.to());
            }
            List<BuildingArea> buildingAreaList =
                    buildingAreaMapper.listByCondition(searchDTO.getPcaList(), pointRanges);
            buildFilterList.addAll(buildingAreaList);
        }
        return buildFilterList;
    }

    private String workOrderName(BillMarketDetailVO saleVO) {
        IntentionSchemeVO intentionSchemeVO = intentionSchemeMapper.getOneById(saleVO.getIntention_scheme_id());
        return intentionSchemeVO.getPlan_name();
    }

}
